package myJs.tools

import myJs.Implicits._
import myJs.Utils
import myJs.Utils._
import myJs.myPkg._
import myJs.myPkg.jquery._
import myJs.tool.Tool
import org.scalajs.dom._
import org.scalajs.dom.ext.Ajax
import org.scalajs.dom.raw.HTMLFormElement
import scalatags.Text.all
import scalatags.Text.all._

import scala.scalajs.js
import scala.scalajs.js.Dynamic.{global => g}
import scala.scalajs.js.annotation.{JSExport, JSExportTopLevel}
import scala.scalajs.js.JSConverters._
import scala.concurrent.ExecutionContext.Implicits.global

/**
 * Created by yz on 16/9/2021
 */
@JSExportTopLevel("PrimerDesign")
object PrimerDesign {

  val formId = "form"

  def formJq = $(s"#${formId}")

  @JSExport("init")
  def init = {
    refreshQueryType
    refreshSpecies
    refreshChr
    bootStrapValidator
    registerEvent
  }

  def refreshChr = {
    val inputName = "chr"
    val species = formJq.find(s":input[name='species']:checked").`val`().toString
    val url = g.jsRoutes.controllers.GenomeController.getAllChrs().url.toString
    Ajax.get(url = s"${url}?species=${species}", headers = Map("Content-Type" -> "application/json")).map { xhr =>
      val rs = xhr.responseText.toJArS
      val array = rs.map { chr =>
        js.Dictionary("text" -> chr, "id" -> chr)
      }
      val options = Select2Options(data=array.toJSArray,multiple=false)
      Tool.getInputByName(inputName).mySelect2(options)
    }
  }

  def refreshQueryType = {
    val names = List("Range", "Seq")
    val html = names.zipWithIndex.map { case (vsName, i) =>
      label(
        marginRight := 15,
        width := 200,
      )(input(
        `class` := "myMethod",
        `type` := "radio",
        name := "method",
        value := vsName,
        if (i == 0) checked else all.raw(""),
      )(vsName)
      )
    }.mkString
    formJq.find(s"#queryType").empty().html(html)
  }

  def refreshSpecies = {
    val speciesMap = Tool.speciesMap
    val html = speciesMap.zipWithIndex.map { case ((k,v), i) =>
      label(
        marginRight := 15,
        width := 250,
      )(input(
        `type` := "radio",
        name := "species",
        value := k,
        if (i == 0) checked else all.raw(""),
        onchange := s"Search.geneIdExampleChange(this)"
      )(v)
      )
    }.mkString
    formJq.find(s"#species").empty().html(html)
  }

  def registerEvent = {
    $(document.body).on("click", s".myRun", "", (y: Element) => {
      myRun
    })
    $(document.body).on("click", s".myShowExample", "", (y: Element) => {
      showExample(y)
    })
    $(document.body).on("change", s".myMethod", "", (y: Element) => {
      methodChange(y)
    })
  }

  def methodChange(y: Element) = {
    val value = $(y).myVal
    $(".eachMethod").hide()
    $(s"#${value.toLowerCase}").show()
  }

  val eg =
    s"""
       |AACCCTAAACCTAAACCCTAAACCCTAAACCCTAAACCCTAACCCTAAACCCTAAACGCTAAACCCTAAACCCTAAACCCTAAACCCTAAACCCTAAACCCTAAACCCTAAACCCTAACCCTAAACCCTAAACCCTAACCCTAAACCCTAAACCCTAAACCCTAACCCTAAACCCTAAACCCTAAACCCTAAACCCTAAACCCTAAACAGCTGACAGTACGATAGATCCACGCGAGAGGAACCGGAGAGACAACGGGATCCAGGCGCCAGCGACGGATCCGGGCGAGAGGGGAGTGGAGATCATGGATCCGTGCGGGAGGGGAAGAAGTCGCCGAATCCGACCCTCCCATCGCCATCGACAGTAGGTCTGCGCGAGAGGGGCACCGGCGCTGGCTCTGAGCGAGATGCAACGCCGGCCGGCTTGGAGAGTAACTCAAGAGAGACAGAATGGAAGATAGAGAACAAGAGAGTGAGAGGATAAGGATATAGACCAGACCACACAATTTTCTCTTCTTTTTAACTTTGTATTAAGATCTTTTATGGAACATCTTTTATTGTTGATATCAAAATAACTGAAACTTATACTTTAATATTTTTTGAGACAAAAAGTAACAATCGTAAAAAAAAGTTCCACGAGAGTTACCCCACCACCCATGTCTCGAGCCGACCAGATCTGCCGCCCACGACCCGAGTCATCGTTGGATCCACCACCCACAACCCGCGACCGAGCTACGCCTCCTGTGGATTGATGCCACTGCCCTGAGCTTCACTGCCGGTACTGTCGCTCGCGACCCGAGCTCCGCCGCTGGTACCGTCGCCCACAACTCAAGCTGTGTTGCCACTAGAGAAAAAGGAGGCGAGAGCGAGGGCAAAACGAGGCAAGGCGTGGCGTGACATGGTGTGACTGGTTGGTCAATTAGCCGACAATGGATCCACCGCCCATGAACCGAGCCACCGTTGGATCCGCCACCCGCAGATTCGGCTGTCCCCTCTCCCTCA
       |""".stripMargin.trim

  def showExample(y: Element) = {
    val inputJq = $(y).parents(".form-group").find(":input")
    inputJq.`val`(eg)
    inputJq.parents(s"#${formId}").bv.revalidateField(inputJq)
  }

  def myRun = {
    val bv = formJq.bv
    val resultJq = $("#result")
    bv.validate()
    val valid = bv.isValid()
    if (valid) {
      val formData = new FormData(document.getElementById(formId).asInstanceOf[HTMLFormElement])
      val element = div(id := "content",
        span(id := "info", "Running...",
          span(id := "progress")), " ",
        img(src := "/assets/images/running2.gif", cls := "runningImage", width := 30, height := 20)
      ).render
      val layerOptions = LayerOptions(icon=16,shade=0.01,time=20000000)
      val index = layer.msg(element, layerOptions)
      val url = g.jsRoutes.controllers.ToolsController.primer().url.toString
      Ajax.post(url = s"${url}", data = formJq.serialize(), headers = Map("Content-Type" -> "application/x-www-form-urlencoded"),
        responseType = "application/json").map { xhr =>
        layer.close(index)
        val rs = xhr.responseText.toJDJAn
        val valid = rs("valid").toB
        if (valid) {
          resultJq.html(rs.myGet("result"))
          resultJq.show()
          val target_top = $("#result").offset().top
          $("html,body").animate(js.Dictionary("scrollTop" -> target_top), JQueryAnimationSettings.duration(800))
        } else {
          g.swal("Error", rs.myGet("message"), "error")
          resultJq.hide()
        }
      }
    }
  }

  def bootStrapValidator = {

    def lessThanF: js.Function = (value: String, bv: BootstrapValidator, jq: JQuery) => {
      val end = Tool.getInputByName("end").myVal
      if (end.isBlank || value.isBlank) {
        true
      } else {
        bv.updateStatus("end", "VALID", "callback")
        value.toInt < end.toInt
      }
    }

    def moreThanF: js.Function = (value: String, bv: BootstrapValidator, jq: JQuery) => {
      val start = Tool.getInputByName("start").myVal
      if (start.isBlank || value.isBlank) {
        true
      } else {
        bv.updateStatus("start", "VALID", "callback")
        start.toInt < value.toInt
      }
    }

    val maxNumber = Double.MaxValue
    val dict: js.Dictionary[js.Any] = js.Dictionary(
      "feedbackIcons" -> js.Dictionary(
        "valid" -> "glyphicon glyphicon-ok",
        "invalid" -> "glyphicon glyphicon-remove",
        "validating" -> "glyphicon glyphicon-refresh",
      ),
      "fields" -> js.Dictionary(
        "start" -> {
          val info = "Start position"
          js.Dictionary(
            "validators" -> js.Dictionary(
              "notEmpty" -> js.Dictionary(
                "message" -> s"${info} is required!"
              ),
              "numeric" -> js.Dictionary(
                "message" -> s"${info} must be number!",
              ),
              "greaterThan" -> js.Dictionary(
                "value" -> 0,
                "message" -> s"${info} must greater than or equal to 0!"
              ),
              "callback" -> js.Dictionary(
                "callback" -> lessThanF,
                "message" -> "Start position must less than end position!"
              ),
            )
          )
        },
        "end" -> {
          val info = "End position"
          js.Dictionary(
            "validators" -> js.Dictionary(
              "notEmpty" -> js.Dictionary(
                "message" -> s"${info} is required!"
              ),
              "numeric" -> js.Dictionary(
                "message" -> s"${info} must be number!",
              ),
              "greaterThan" -> js.Dictionary(
                "value" -> 0,
                "message" -> s"${info} must greater than or equal to 0!"
              ),
              "callback" -> js.Dictionary(
                "callback" -> moreThanF,
                "message" -> "End position must greater than start position!"
              ),
            )
          )
        },
        "SEQUENCE_TARGET" -> {
          val info = "Sequence target"
          js.Dictionary(
            "validators" -> js.Dictionary(
              "notEmpty" -> js.Dictionary(
                "message" -> s"${info} is required!"
              ),
            )
          )
        },
        "PRIMER_OPT_SIZE" -> {
          val info = "Optimal primer size"
          js.Dictionary(
            "validators" -> js.Dictionary(
              "notEmpty" -> js.Dictionary(
                "message" -> s"${info} is required!"
              ),
              "integer" -> js.Dictionary(
                "message" -> s"${info} must be integer！",
              ),
            )
          )
        },
        "PRIMER_MAX_SIZE" -> {
          val info = ">Max primer size"
          js.Dictionary(
            "validators" -> js.Dictionary(
              "notEmpty" -> js.Dictionary(
                "message" -> s"${info} is required!"
              ),
              "integer" -> js.Dictionary(
                "message" -> s"${info} must be integer！",
              ),
            )
          )
        },
        "PRIMER_MIN_SIZE" -> {
          val info = "Min primer size"
          js.Dictionary(
            "validators" -> js.Dictionary(
              "notEmpty" -> js.Dictionary(
                "message" -> s"${info} is required!"
              ),
              "integer" -> js.Dictionary(
                "message" -> s"${info} must be integer！",
              ),
            )
          )
        },
        "PRIMER_OPT_TM" -> {
          val info = "Optimal primer TM"
          js.Dictionary(
            "validators" -> js.Dictionary(
              "notEmpty" -> js.Dictionary(
                "message" -> s"${info} is required!"
              ),
              "numeric" -> js.Dictionary(
                "message" -> s"${info} must be number!",
              ),
            )
          )
        },
        "PRIMER_MAX_TM" -> {
          val info = "Max primer TM"
          js.Dictionary(
            "validators" -> js.Dictionary(
              "notEmpty" -> js.Dictionary(
                "message" -> s"${info} is required!"
              ),
              "numeric" -> js.Dictionary(
                "message" -> s"${info} must be number!",
              ),
            )
          )
        },
        "PRIMER_MIN_TM" -> {
          val info = "Min primer TM"
          js.Dictionary(
            "validators" -> js.Dictionary(
              "notEmpty" -> js.Dictionary(
                "message" -> s"${info} is required!"
              ),
              "numeric" -> js.Dictionary(
                "message" -> s"${info} must be number!",
              ),
            )
          )
        },
        "PRIMER_OPT_GC_PERCENT" -> {
          val info = "Optimal GC percent"
          js.Dictionary(
            "validators" -> js.Dictionary(
              "notEmpty" -> js.Dictionary(
                "message" -> s"${info} is required!"
              ),
              "numeric" -> js.Dictionary(
                "message" -> s"${info} must be number!",
              ),
            )
          )
        },
        "PRIMER_MAX_GC" -> {
          val info = "Max GC percent"
          js.Dictionary(
            "validators" -> js.Dictionary(
              "notEmpty" -> js.Dictionary(
                "message" -> s"${info} is required!"
              ),
              "numeric" -> js.Dictionary(
                "message" -> s"${info} must be number!",
              ),
            )
          )
        },
        "PRIMER_MIN_GC" -> {
          val info = "Min GC percent"
          js.Dictionary(
            "validators" -> js.Dictionary(
              "notEmpty" -> js.Dictionary(
                "message" -> s"${info} is required!"
              ),
              "numeric" -> js.Dictionary(
                "message" -> s"${info} must be number!",
              ),
            )
          )
        },
        "PRIMER_MAX_NS_ACCEPTED" -> {
          val info = "Max #N's accepted"
          js.Dictionary(
            "validators" -> js.Dictionary(
              "notEmpty" -> js.Dictionary(
                "message" -> s"${info} is required!"
              ),
              "integer" -> js.Dictionary(
                "message" -> s"${info} must be integer！",
              ),
            )
          )
        },
        "PRIMER_MAX_POLY_X" -> {
          val info = "Max Poly-X"
          js.Dictionary(
            "validators" -> js.Dictionary(
              "notEmpty" -> js.Dictionary(
                "message" -> s"${info} is required!"
              ),
              "integer" -> js.Dictionary(
                "message" -> s"${info} must be integer！",
              ),
            )
          )
        },
        "PRIMER_INTERNAL_MAX_POLY_X" -> {
          val info = "Max Internal Poly-X"
          js.Dictionary(
            "validators" -> js.Dictionary(
              "notEmpty" -> js.Dictionary(
                "message" -> s"${info} is required!"
              ),
              "integer" -> js.Dictionary(
                "message" -> s"${info} must be integer！",
              ),
            )
          )
        },
        "PRIMER_MAX_SELF_ANY" -> {
          val info = "Max Internal Poly-X"
          js.Dictionary(
            "validators" -> js.Dictionary(
              "notEmpty" -> js.Dictionary(
                "message" -> s"${info} is required!"
              ),
              "numeric" -> js.Dictionary(
                "message" -> s"${info} must be numeric！",
              ),
            )
          )
        },
        "PRIMER_MAX_SELF_END" -> {
          val info = "Max 3' Self Complementarity"
          js.Dictionary(
            "validators" -> js.Dictionary(
              "notEmpty" -> js.Dictionary(
                "message" -> s"${info} is required!"
              ),
              "numeric" -> js.Dictionary(
                "message" -> s"${info} must be numeric！",
              ),
            )
          )
        },
        "PRIMER_PAIR_MAX_COMPL_ANY" -> {
          val info = "Max Pair Complementarity"
          js.Dictionary(
            "validators" -> js.Dictionary(
              "notEmpty" -> js.Dictionary(
                "message" -> s"${info} is required!"
              ),
              "numeric" -> js.Dictionary(
                "message" -> s"${info} must be numeric！",
              ),
            )
          )
        },
        "PRIMER_PAIR_MAX_COMPL_END" -> {
          val info = "Max 3' Pair Complementarity"
          js.Dictionary(
            "validators" -> js.Dictionary(
              "notEmpty" -> js.Dictionary(
                "message" -> s"${info} is required!"
              ),
              "numeric" -> js.Dictionary(
                "message" -> s"${info} must be numeric！",
              ),
            )
          )
        },
        "queryText" -> {
          val info = "FASTA sequences"
          js.Dictionary(
            "validators" -> js.Dictionary(
              "notEmpty" -> js.Dictionary(
                "message" -> s"${info} is required!"
              ),
            )
          )
        },
      )
    )
    formJq.bootstrapValidator(dict)
  }

}
